<template>
  <div
    class="edit-wrapper"
    ref="editWrapper"
    @mousedown.stop="startMove"
    :class="{ active: active, hidden: hidden }"
    :style="wrapperPropsStyle"
    @click="checkActive"
  >
    <div class="resizers">
      <div
        class="resizer top-left"
        @mousedown.stop="startResizeHandle('top-left')"
      ></div>
      <div
        class="resizer top-right"
        @mousedown.stop="startResizeHandle('top-right')"
      ></div>
      <div
        class="resizer bottom-left"
        @mousedown.stop="startResizeHandle('bottom-left')"
      ></div>
      <div
        class="resizer bottom-right"
        @mousedown.stop="startResizeHandle('bottom-right')"
      ></div>
    </div>
    <slot></slot>
  </div>
</template>

<script lang="ts">
export default {
  name: 'EditorWrapper',
}
</script>
<script setup lang="ts">
import { computed, nextTick, ref } from 'vue'
import { pick } from 'lodash-es'
//方向
type Direction = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
interface IDirection {
  left: number
  top: number
  right: number
  bottom: number
}
const props = defineProps({
  id: {
    type: String,
    required: true,
  },
  active: {
    type: Boolean,
    default: false,
  },
  hidden: {
    type: Boolean,
    default: false,
  },
  props: {
    type: Object,
    default: () => ({}),
  },
})

const emits = defineEmits(['set-active', 'change'])
const editWrapper = ref<null | HTMLElement>(null)
// 定义一个gap对象，用于存储鼠标点击位置与元素左上角的距离
const gap = {
  x: 0,
  y: 0,
}
let isMoving = false
// 定义一个startMove函数，用于处理鼠标按下事件
const startMove = (e: MouseEvent) => {
  checkActive()
  // 获取当前元素
  const currentElement = editWrapper.value
  if (currentElement) {
    // 获取当前元素的左上角坐标
    const { left, top } = currentElement.getBoundingClientRect()
    // 计算鼠标点击位置与元素左上角的距离
    gap.x = e.clientX - left
    gap.y = e.clientY - top
  }
  // 定义一个wrapperMoveEnd函数，用于处理鼠标松开事件
  const wrapperMoveEnd = () => {
    // 移除mousemove事件监听
    document.removeEventListener('mousemove', handleWrapperMove)
    if (isMoving) {
      // 触发change事件
      emits('change', {
        left: parseInt(editWrapper.value?.style.left as string, 10),
        top: parseInt(editWrapper.value?.style.top as string, 10),
        id: props.id,
      })
      isMoving = false
    }
    // 移除mouseup事件监听
    nextTick(() => {
      document.removeEventListener('mouseup', wrapperMoveEnd)
    })
  }
  // 添加mousemove事件监听
  document.addEventListener('mousemove', handleWrapperMove)
  document.addEventListener('mouseup', wrapperMoveEnd)
}
// 定义一个computedLeftOrTop函数，用于计算元素的新位置
const computedLeftOrTop = (e: MouseEvent) => {
  // 获取容器元素
  const container = document.getElementById('canvas-area') as HTMLElement
  // 计算元素的新位置
  return {
    left: e.clientX - gap.x - container.offsetLeft,
    top: e.clientY - gap.y - container.offsetTop + container.scrollTop,
  }
}
const computedReSize = (
  direction: string,
  e: MouseEvent,
  position: IDirection
) => {
  // 获取canvas-area元素
  const container = document.getElementById('canvas-area') as HTMLElement
  // 获取元素的位置信息
  const { top, left, right, bottom } = position
  // 获取鼠标的位置信息
  const { clientX, clientY } = e
  // 计算元素的新宽度
  const rightWidth = clientX - left
  const leftWidth = right - clientX
  // 计算元素的新高度
  const bottomHeight = clientY - top
  const topHeight = bottom - clientY
  // 计算元素的新位置
  const topOffset = clientY - container.offsetTop + container.scrollTop
  const leftOffset = clientX - container.offsetLeft
  // 根据方向返回元素的新位置和大小
  switch (direction) {
    case 'top-left':
      return {
        left: leftOffset,
        top: topOffset,
        width: leftWidth,
        height: topHeight,
      }
    case 'top-right':
      return {
        top: topOffset,
        width: rightWidth,
        height: topHeight,
      }
    case 'bottom-left':
      return {
        left: leftOffset,
        width: leftWidth,
        height: bottomHeight,
      }
    case 'bottom-right':
      return {
        width: rightWidth,
        height: bottomHeight,
      }
    default:
      return {}
  }
}
// 定义一个handleWrapperMove函数，用于处理鼠标移动事件
const handleWrapperMove = (e: MouseEvent) => {
  // 计算元素的新位置
  const { left, top } = computedLeftOrTop(e)
  // 设置元素的新位置
  if (editWrapper.value) {
    isMoving = true
    editWrapper.value.style.left = `${left}px`
    editWrapper.value.style.top = `${top}px`
  }
}

/**定义一个startResize函数，用于处理鼠标拖拽dom元素四个点改变大小的回调*/
const startResizeHandle = (direction: Direction) => {
  // 获取当前元素
  const currentElement = editWrapper.value as HTMLElement
  // 获取元素的位置信息
  const { left, top, right, bottom } = currentElement.getBoundingClientRect()

  // 定义一个handleMove函数，用于处理鼠标移动事件
  const handleMove = (e: MouseEvent) => {
    // 计算元素的新大小
    if (currentElement) {
      const size = computedReSize(direction, e, { left, top, right, bottom })

      if (size) {
        // 设置元素的新大小
        currentElement.style.width = `${size.width}px`
        currentElement.style.height = `${size.height}px`
        // 设置元素的新位置
        if (size.left) {
          currentElement.style.left = `${size.left}px`
        }
        if (size.top) {
          currentElement.style.top = `${size.top}px`
        }
      }
    }
  }
  // 定义一个handleMoveEnd函数，用于处理鼠标松开事件
  const handleMoveEnd = (e: MouseEvent) => {
    // 移除鼠标移动事件监听
    document.removeEventListener('mousemove', handleMove)
    // 计算元素的新大小
    const size = computedReSize(direction, e, { left, top, right, bottom })

    // 触发change事件
    emits('change', { ...size, id: props.id })
    // 移除鼠标松开事件监听
    nextTick(() => {
      document.removeEventListener('mouseup', handleMoveEnd)
    })
  }
  // 添加鼠标移动事件监听
  document.addEventListener('mousemove', handleMove)
  // 添加鼠标松开事件监听
  document.addEventListener('mouseup', handleMoveEnd)
}

const checkActive = () => {
  emits('set-active', props.id)
}
const wrapperPropsStyle = computed(() => {
  return pick(props.props, ['width', 'height', 'top', 'left', 'position'])
})
</script>

<style scoped lang="scss">
.edit-wrapper {
  padding: 0px;
  cursor: pointer;
  border: 1px solid transparent;
  user-select: none;
  box-sizing: content-box !important;
}
.edit-wrapper > * {
  position: static !important;
  width: 100% !important;
  height: 100% !important;
}
.edit-wrapper:hover {
  border: 1px dashed #ccc;
}
.edit-wrapper.hidden {
  display: none;
}
.edit-wrapper.active {
  border: 1px solid #1890ff;
  user-select: none;
  z-index: 1500 !important;
}
.edit-wrapper .resizers {
  display: none;
}
.edit-wrapper.active .resizers {
  display: block;
  width: auto !important;
  height: auto !important;
}
.edit-wrapper.active .resizers .resizer {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #fff;
  border: 3px solid #1890ff;
  position: absolute;
}
.edit-wrapper .resizers .resizer.top-left {
  left: -5px;
  top: -5px;
  cursor: nwse-resize;
}
.edit-wrapper .resizers .resizer.top-right {
  right: -5px;
  top: -5px;
  cursor: nesw-resize;
}
.edit-wrapper .resizers .resizer.bottom-left {
  left: -5px;
  bottom: -5px;
  cursor: nesw-resize;
}
.edit-wrapper .resizers .resizer.bottom-right {
  right: -5px;
  bottom: -5px;
  cursor: nwse-resize;
}
</style>
<!-- 

-->
